#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <oci.h>


typedef struct {
	OCIEnv		*envhp;			/* Environment handle 	*/
	OCIError	*errhp;			/* Error handle		*/
	OCIServer	*srvhp;			/* Server handle	*/
	OCISession	*usrhp;			/* User handle		*/
	OCISvcCtx	*svchp;			/* Service Context	*/
} ServerContext;


ServerContext *Connect(char *user, char *password, char *database) {
	ServerContext *sc;
	int	userlen;
	int	passwordlen;
	int	databaselen;
	sword	status;
	sword	credentials = OCI_CRED_RDBMS;

        userlen = strlen(user);
        passwordlen = strlen(password);
        databaselen = strlen(database);

        sc = (ServerContext *)malloc(sizeof(ServerContext));

	sc->envhp = NULL;
	sc->errhp = NULL;
	sc->svchp = NULL;
	sc->srvhp = NULL;
	sc->usrhp = NULL;

	status = OCIEnvCreate(&sc->envhp, OCI_THREADED, NULL, NULL, NULL, NULL,
                              0, NULL);
	assert(status == OCI_SUCCESS);

	/* Allocate the error handle */
	status = OCIHandleAlloc(sc->envhp, (dvoid **)&sc->errhp,
                                OCI_HTYPE_ERROR, 0, NULL);
	assert(status == OCI_SUCCESS);

	/* Allocate the server handle */
	status = OCIHandleAlloc(sc->envhp, (dvoid **) &sc->srvhp,
                                OCI_HTYPE_SERVER, 0, NULL);
	assert(status == OCI_SUCCESS);

	/* Connect to database */
	status = OCIServerAttach(sc->srvhp, sc->errhp, database, databaselen,
                                 OCI_DEFAULT);
	assert(status == OCI_SUCCESS);

	/* Allocate a service context */
	status = OCIHandleAlloc(sc->envhp, (dvoid **) &sc->svchp,
                                OCI_HTYPE_SVCCTX, 0, NULL);
	assert(status == OCI_SUCCESS);

	/* Attach the server to the service context */
	status = OCIAttrSet(sc->svchp, OCI_HTYPE_SVCCTX, (dvoid *) sc->srvhp,
                            (ub4) 0, OCI_ATTR_SERVER, sc->errhp);
	assert(status == OCI_SUCCESS);

	/* Allocate a session */
	status = OCIHandleAlloc(sc->envhp, (dvoid **) &sc->usrhp,
                                OCI_HTYPE_SESSION, 0, NULL);
	assert(status == OCI_SUCCESS);

	/* Were credentials provided? if so, set them in the session */
        status = OCIAttrSet(sc->usrhp, OCI_HTYPE_SESSION,
                            (dvoid *) user, (ub4) userlen,
                            OCI_ATTR_USERNAME, sc->errhp);
        assert(status == OCI_SUCCESS);

        status = OCIAttrSet(sc->usrhp, OCI_HTYPE_SESSION,
                            (dvoid *) password, (ub4) passwordlen,
                            OCI_ATTR_PASSWORD, sc->errhp);
        assert(status == OCI_SUCCESS);
	
	/* Now begin the session */
	status = OCISessionBegin(sc->svchp, sc->errhp, sc->usrhp,
                                 credentials, OCI_DEFAULT);
	assert(status == OCI_SUCCESS);

	/* Now attach the session to the service context */
	status = OCIAttrSet(sc->svchp, OCI_HTYPE_SVCCTX,
                            sc->usrhp, 0, OCI_ATTR_SESSION, sc->errhp);
	assert(status == OCI_SUCCESS);

	return sc;
}

/*
** ServerContext_dealloc
**
** Called when a server context goes out of scope; hang up the connection
** to the database!
*/

void ServerContext_dealloc(ServerContext *self) {
	sword status;

	/* End the session */
	assert(self->svchp);
        status = OCISessionEnd(self->svchp, self->errhp, self->usrhp,
                               OCI_DEFAULT);
        assert(status == OCI_SUCCESS);

	/* Disconnect from the server */
	assert(self->srvhp);
        status = OCIServerDetach(self->srvhp, self->errhp, OCI_DEFAULT);
        assert(status == OCI_SUCCESS);

	/* Deallocate the session handle */
        assert(self->usrhp);
        status = OCIHandleFree(self->usrhp, OCI_HTYPE_SESSION);
        assert(status == OCI_SUCCESS);

	/* Deallocate the server handle */
        assert(self->srvhp);
        status = OCIHandleFree(self->srvhp, OCI_HTYPE_SERVER);
        assert(status == OCI_SUCCESS);

	/* Deallocate the service context handle */
	assert(self->svchp);
        status = OCIHandleFree(self->svchp, OCI_HTYPE_SVCCTX);
        assert(status == OCI_SUCCESS);

	/* Deallocate the error handle */
        assert(self->errhp);
        status = OCIHandleFree(self->errhp, OCI_HTYPE_ERROR);
        assert(status == OCI_SUCCESS);

	/* Deallocate the environment handle */
        assert(self->envhp);
        status = OCIHandleFree(self->envhp, OCI_HTYPE_ENV);
        assert(status == OCI_SUCCESS);

        free(self);
}

sword dynamicBindIn(dvoid *context, OCIBind *bindp, ub4 iter, ub4 index,
	dvoid **bufpp, ub4 *alenp, ub1 *piecep, dvoid **indpp) {

	OCIStmt **stmtp = (OCIStmt **) context;

	fprintf(stderr,"dynamicBindIn(%08x, %08x, %d, %d, %08x, %08x, %08x, "
		"%08x)\n", (int) context, (int) bindp, iter, index,
		(int) bufpp, (int) alenp, (int) piecep, (int) indpp);

	*bufpp = stmtp;
	*alenp = 0;
	*piecep = OCI_ONE_PIECE;
	*indpp = NULL;

	fprintf(stderr,"\treturning *bufpp = %08x\n", (int) *bufpp);

	return OCI_CONTINUE;
}

sword dynamicBindOut(dvoid *context, OCIBind *bindp, ub4 iter, ub4 index,
	dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp,
	ub2 **rcodepp) {

	OCIStmt **stmtp = (OCIStmt **) context;

	fprintf(stderr,"dynamicBindOut(%08x, %08x, %d, %d, %08x, %08x, %08x, "
		"%08x, %08x,)\n", (int) context, (int) bindp, iter, index,
		(int) bufpp, (int) alenpp, (int) piecep, (int) indpp,
		(int) rcodepp);

/*
	*bufpp = stmtp;
	*alenp = 0;
	*piecep = OCI_ONE_PIECE;
	*indpp = NULL;
*/

	return OCI_CONTINUE;
}

int main(int argc, char *argv[]) {
	char *user;
	char *password;
	char *database;
	char *statement = "BEGIN EMP_ACTIONS.FINDMGR(:1, :2); END;";
	int statementl;
	int mgrid = 7839;
	sword status;
	OCIStmt *stmt1;
	OCIStmt *refcur;
	ServerContext *db;
	OCIBind *binds[2];

	assert(argc == 4);
	user = argv[1];
	password = argv[2];
	database = argv[3];

	fprintf(stderr, "Connecting to db.\n");
	db = Connect(user, password, database);

	fprintf(stderr, "Allocating statement handles\n");
	status = OCIHandleAlloc(db->envhp, (dvoid **) &stmt1,
		 OCI_HTYPE_STMT, 0, 0);
	assert(status == OCI_SUCCESS);
	status = OCIHandleAlloc(db->envhp, (dvoid **) &refcur,
		 OCI_HTYPE_STMT, 0, 0);
	assert(status == OCI_SUCCESS);

	fprintf(stderr, "Refcur: %08x (stored at %08x)\n", (int) refcur,
		(int) &refcur);


	statementl = strlen(statement);

	fprintf(stderr, "Preparing statement\n");
	status = OCIStmtPrepare(stmt1, db->errhp, statement, statementl,
		OCI_NTV_SYNTAX, OCI_DEFAULT);
	assert(status == OCI_SUCCESS);

	fprintf(stderr, "Binding\n");
	status = OCIBindByPos(stmt1, &binds[0], db->errhp, 1, &mgrid, 0,
		SQLT_INT, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
	assert(status == OCI_SUCCESS);

	fprintf(stderr,"OCIBindByPos(%08x, %08x, %08x, 2, NULL, 0, %d, "
		"NULL, NULL, NULL, 0, NULL, %d)\n", (int) stmt1,
		(int) &binds[1], (int) db->errhp, SQLT_RSET, OCI_DATA_AT_EXEC);

	status = OCIBindByPos(stmt1, &binds[1], db->errhp, 2, NULL, 0,
		SQLT_RSET, NULL, NULL, NULL, 0, NULL, OCI_DATA_AT_EXEC);
	assert(status == OCI_SUCCESS);

	fprintf(stderr,"OCIBindDynamic(%08x, %08x, %08x, %08x, %08x, %08x)\n",
		(int) binds[1], (int) db->errhp, (int) refcur,
		(int) dynamicBindIn, (int) refcur, (int) dynamicBindOut);
	
	status = OCIBindDynamic(binds[1], db->errhp,
		(dword *) refcur, (OCICallbackInBind) dynamicBindIn,
		(dword *) refcur, (OCICallbackOutBind) dynamicBindOut);
	assert(status == OCI_SUCCESS);
	
	fprintf(stderr, "Executing\n");
	status = OCIStmtExecute(db->svchp, stmt1, db->errhp, 1, 0, NULL, NULL,
		OCI_DEFAULT);
	assert(status == OCI_SUCCESS);

	fprintf(stderr, "Disconnecting db.\n");
	ServerContext_dealloc(db);
	return 0;
}

